#include "apue.h"
#include <unistd.h>

static volatile sig_atomic_t sigval;
struct sigaction wait_action;
sigset_t waitmask, oldmask, emptymask;

void sig_usr(int) {
    sigval= 1;
}

void TELL_WAIT() {
    wait_action.sa_handler= sig_usr;  
    sigemptyset(&wait_action.sa_mask);
    wait_action.sa_flags= 0;
    if (sigaction(SIGUSR1, &wait_action, NULL) < 0)
        err_sys("sigaction(SIGUSR1) error");
    if (sigaction(SIGUSR2, &wait_action, NULL) < 0)
        err_sys("sigaction(SIGUSR2) error");

    sigemptyset(&waitmask);
    sigemptyset(&emptymask);
    sigaddset(&waitmask, SIGUSR1);
    sigaddset(&waitmask, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &waitmask, &oldmask) < 0)
        err_sys("SIG_BLOCK error");
}

void TELL_CHILD(pid_t pid) {
    kill(pid, SIGUSR1);
}

void TELL_PARENT(pid_t pid) {
    kill(pid, SIGUSR2);
}

void WAIT_CHILD() {
    while (sigval != 1)
        sigsuspend(&emptymask);
    sigval= 0;
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

void WAIT_PARENT() {
    while (sigval != 1) 
        sigsuspend(&emptymask);
    sigval= 0;
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
}

int file_incr(FILE *);

int main(void) {
    FILE *fp;
    pid_t pid= 0;
    int zero= 0;
    int val;
    if ((fp= fopen("practice_10.6.val", "w+")) == NULL) 
        err_sys("fopen error");
    if (fwrite(&zero, sizeof(zero), 1, fp) != 1)
        err_sys("fwrite error");
     
    TELL_WAIT();

    if ((pid= fork()) < 0)
        err_sys("fork error");
    if (pid == 0) {
        do {
            printf("child increment val to %d\n", val= file_incr(fp));
            TELL_PARENT(getppid());
            WAIT_PARENT();
        } while (val < 100);

    } else {
        do {
            WAIT_CHILD();
            printf("parent increment val to %d\n", val= file_incr(fp));
            TELL_CHILD(pid);
        } while (val < 100);
        
    }

}

int file_incr(FILE *fp) {
    int val;
    if (fseek(fp, 0, SEEK_SET) != 0)
        err_sys("1fseek error");
    if (fread(&val, sizeof(val), 1, fp) != 1)
        err_sys("fread error");

    val++;
    if (fseek(fp, 0, SEEK_SET) != 0)
        err_sys("2fseek error");
    if(fwrite(&val, sizeof(val), 1, fp) != 1)
        err_sys("fwrite error");
    // 这里必须要刷新缓冲，否则会有两种异常
    // 1. 父子进程在某些循环获取的值没有变化
    // 2. fseek 报错，invalid argument(22)
    fflush(fp);
    return val;
}



